package photonpay

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	"gitee.com/wangshh_123/cross-pay-go/src/config"
	"gitee.com/wangshh_123/cross-pay-go/src/database"
	"gitee.com/wangshh_123/cross-pay-go/src/platform/common"
)

var (
	// 单例实例
	instance *PhotonPayClient
	once     sync.Once
	initErr  error
)

// TokenInfo 存储token信息
type TokenInfo struct {
	Token            string `json:"token"`
	RefreshToken     string `json:"refreshToken"`
	ExpiresIn        int64  `json:"expiresIn"`        // 过期时间（秒）
	RefreshExpiresIn int64  `json:"refreshExpiresIn"` // 刷新token过期时间（秒）
	ExpiresAt        int64  `json:"expiresAt"`        // 过期时间戳（毫秒）
}

// PhotonPayClient 光子易支付平台客户端
// 用于处理与光子易支付平台的所有交互
type PhotonPayClient struct {
	config     *common.PlatformConfig // 平台配置信息
	httpClient *http.Client           // HTTP客户端
	utils      *PhotonPayUtils        // 工具类实例
	refreshMu  sync.Mutex             // 刷新互斥锁，防止并发刷新
	tokenCache *TokenCache            // 内存缓存
}

type TokenCache struct {
	token     string
	expiresAt int64
	mu        sync.RWMutex
}

func NewTokenCache() *TokenCache {
	return &TokenCache{}
}

func (tc *TokenCache) Get() (string, bool) {
	tc.mu.RLock()
	defer tc.mu.RUnlock()

	if tc.token == "" || time.Now().UnixMilli() >= tc.expiresAt {
		return "", false
	}
	return tc.token, true
}

func (tc *TokenCache) Set(token string, expiresAt int64) {
	tc.mu.Lock()
	defer tc.mu.Unlock()

	tc.token = token
	tc.expiresAt = expiresAt
}

// GetPhotonPayClient 获取PhotonPayClient单例实例
// 参数:
//   - config: 平台配置信息
//
// 返回:
//   - *PhotonPayClient: 客户端实例
//   - error: 初始化过程中的错误
func GetPhotonPayClient(config *common.PlatformConfig) *PhotonPayClient {
	once.Do(func() {
		utils, err := NewPhotonPayUtils()
		if err != nil {
			initErr = fmt.Errorf("初始化PhotonPayUtils失败: %v", err)
			return
		}

		instance = &PhotonPayClient{
			config:     config,
			httpClient: &http.Client{Timeout: 30 * time.Second},
			utils:      utils,
			tokenCache: NewTokenCache(),
		}
	})

	if initErr != nil {
		panic(fmt.Sprintf("PhotonPayClient初始化失败: %v", initErr))
	}

	return instance
}

// NewPhotonPayClient 创建光子易支付平台客户端（已废弃，请使用GetPhotonPayClient）
// 参数:
//   - config: 平台配置信息
//
// 返回:
//   - *PhotonPayClient: 客户端实例
func NewPhotonPayClient(config *common.PlatformConfig) *PhotonPayClient {
	fmt.Println("警告: NewPhotonPayClient已废弃，请使用GetPhotonPayClient")
	return GetPhotonPayClient(config)
}

// GetToken 获取当前访问令牌
// 返回:
//   - string: 当前有效的访问令牌
func (c *PhotonPayClient) GetToken() (string, error) {
	// 首先从内存缓存中获取
	if token, ok := c.tokenCache.Get(); ok {
		return token, nil
	}

	// 如果内存缓存中没有，从Redis获取
	tokenInfo, err := c.getTokenFromRedis()
	if err != nil {
		// Redis中没有，获取新token
		return c.refreshToken()
	}

	// 检查token是否过期
	now := time.Now().UnixMilli()
	fmt.Println("tokenInfo.ExpiresAt", tokenInfo.ExpiresIn)
	if tokenInfo.ExpiresIn <= now {
		// token已过期，刷新token
		return c.refreshToken()
	}

	// token有效，更新内存缓存并返回
	c.tokenCache.Set(tokenInfo.Token, tokenInfo.ExpiresAt)
	return tokenInfo.Token, nil
}

// refreshToken 刷新token
// 返回:
//   - string: 新的访问令牌
//   - error: 刷新过程中的错误
func (c *PhotonPayClient) refreshToken() (string, error) {
	c.refreshMu.Lock()
	defer c.refreshMu.Unlock()

	// 再次检查内存缓存
	if token, ok := c.tokenCache.Get(); ok {
		return token, nil
	}

	// 获取新token
	token, refreshToken, expiresIn, refreshExpiresIn, err := c.utils.GetAccessToken()
	if err != nil {
		return "", fmt.Errorf("获取访问令牌失败: %v", err)
	}

	// 确保expiresIn是正数
	if expiresIn <= 0 {
		expiresIn = 7200 // 默认2小时
	}

	// 计算过期时间戳（毫秒）
	now := time.Now()
	expiresAt := now.Add(time.Duration(expiresIn) * time.Second).UnixMilli()

	// 构造token信息
	tokenInfo := TokenInfo{
		Token:            token,
		RefreshToken:     refreshToken,
		ExpiresIn:        expiresIn,
		RefreshExpiresIn: refreshExpiresIn,
		ExpiresAt:        expiresAt,
	}

	// 将token信息存储到Redis
	tokenJSON, err := json.Marshal(tokenInfo)
	if err != nil {
		return "", fmt.Errorf("序列化token信息失败: %v", err)
	}

	// 使用Redis存储token信息
	err = database.RedisClient.Set(context.Background(), "photonpay:token", tokenJSON, time.Duration(expiresIn)*time.Second).Err()
	if err != nil {
		return "", fmt.Errorf("存储token到Redis失败: %v", err)
	}

	// 更新内存缓存
	c.tokenCache.Set(token, expiresAt)

	if config.GlobalConfig.Server.Mode == "debug" {
		fmt.Printf("[PhotonPay] 新token已保存，过期时间: %d秒\n", expiresIn)
	}

	return token, nil
}

// doRequest 发送HTTP请求
// 参数:
//   - ctx: 上下文
//   - method: HTTP方法
//   - path: 请求路径
//   - body: 请求体
//
// 返回:
//   - []byte: 响应数据
//   - error: 请求过程中的错误
func (c *PhotonPayClient) doRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) {
	var lastErr error
	for i := 0; i <= config.GlobalConfig.PhotonPay.Token.MaxRetries; i++ {
		// 获取token
		token, err := c.GetToken()
		if err != nil {
			lastErr = fmt.Errorf("获取token失败: %v", err)
			continue
		}

		// 构造请求URL
		url := c.config.GatewayURL + path
		if config.GlobalConfig.Server.Mode == "debug" {
			fmt.Printf("[PhotonPay] 请求URL: %s\n", url)
		}

		// 构造请求体
		var reqBody []byte
		if body != nil {
			reqBody, err = json.Marshal(body)
			if err != nil {
				lastErr = fmt.Errorf("序列化请求体失败: %v", err)
				continue
			}
		}

		// 创建请求
		req, err := http.NewRequest(method, url, bytes.NewBuffer(reqBody))
		if err != nil {
			lastErr = fmt.Errorf("创建请求失败: %v", err)
			continue
		}

		// 设置请求头
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("X-Pd-Token", token)

		if config.GlobalConfig.Server.Mode == "debug" {
			fmt.Printf("[PhotonPay] 请求头: %v\n", req.Header)
		}

		// 发送请求
		resp, err := c.httpClient.Do(req)
		if err != nil {
			lastErr = fmt.Errorf("发送请求失败: %v", err)
			continue
		}

		// 读取响应体
		respBody, err := io.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			lastErr = fmt.Errorf("读取响应体失败: %v", err)
			continue
		}

		// 检查响应状态码
		if resp.StatusCode == http.StatusUnauthorized {
			// token无效，清除缓存并刷新token
			c.tokenCache.Set("", 0)
			if err := database.RedisClient.Del(context.Background(), "photonpay:token").Err(); err != nil {
				if config.GlobalConfig.Server.Mode == "debug" {
					fmt.Printf("[PhotonPay] 清除Redis token失败: %v\n", err)
				}
			}
			continue
		}

		if resp.StatusCode != http.StatusOK {
			lastErr = fmt.Errorf("请求失败，状态码: %d, 响应: %s", resp.StatusCode, string(respBody))
			continue
		}

		return respBody, nil
	}

	return nil, fmt.Errorf("所有重试都失败了: %v", lastErr)
}

// Get 发送GET请求
func (c *PhotonPayClient) Get(ctx context.Context, path string) ([]byte, error) {
	return c.doRequest(ctx, http.MethodGet, path, nil)
}

// Post 发送POST请求
func (c *PhotonPayClient) Post(ctx context.Context, path string, body interface{}) ([]byte, error) {
	return c.doRequest(ctx, http.MethodPost, path, body)
}

// Put 发送PUT请求
func (c *PhotonPayClient) Put(ctx context.Context, path string, body interface{}) ([]byte, error) {
	return c.doRequest(ctx, http.MethodPut, path, body)
}

// Delete 发送DELETE请求
func (c *PhotonPayClient) Delete(ctx context.Context, path string) ([]byte, error) {
	return c.doRequest(ctx, http.MethodDelete, path, nil)
}

// PostWithRawBody 发送 POST 请求，bodyBytes 用于签名和请求体
func (c *PhotonPayClient) PostWithRawBody(ctx context.Context, path string, bodyBytes []byte) ([]byte, error) {
	sign, err := c.utils.GenerateSign(bodyBytes)
	if err != nil {
		return nil, err
	}
	token, err := c.GetToken()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequestWithContext(ctx, "POST", c.config.GatewayURL+path, bytes.NewReader(bodyBytes))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-PD-TOKEN", token)
	req.Header.Set("X-PD-SIGN", sign)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBytes))
	}
	return respBytes, nil
}

// VerifySign 验证签名
// 参数:
//   - params: 请求参数
//   - signature: 签名
//
// 返回:
//   - bool: 签名是否有效
func (c *PhotonPayClient) VerifySign(params map[string]interface{}, signature string) bool {
	// 将参数转换为JSON字符串
	jsonData, err := json.Marshal(params)
	if err != nil {
		return false
	}

	// 使用工具类生成签名
	expectedSign, err := c.utils.GenerateSign(jsonData)
	if err != nil {
		return false
	}

	// 比较签名
	return expectedSign == signature
}

func (c *PhotonPayClient) getTokenFromRedis() (*TokenInfo, error) {
	tokenJSON, err := database.RedisClient.Get(context.Background(), "photonpay:token").Bytes()
	if err != nil {
		return nil, fmt.Errorf("获取token失败: %v", err)
	}

	var tokenInfo TokenInfo
	if err := json.Unmarshal(tokenJSON, &tokenInfo); err != nil {
		return nil, fmt.Errorf("解析token信息失败: %v", err)
	}

	return &tokenInfo, nil
}
